package DyingBaby.backend.table;

import DyingBaby.backend.catalog.ColumnDef;
import DyingBaby.backend.catalog.ColumnType;
import DyingBaby.backend.catalog.TableSchema;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * RowCodec 类负责表记录的行级编码和解码。
 * 它将内存中的记录(Map<String, Object>)与二进制字节数组之间进行相互转换。
 * 这种二进制格式比JSON等文本格式更紧凑、更高效，适合数据库底层存储。
 */
class RowCodec {
    /**
     * 将一条记录编码为二进制字节数组
     * @param schema 表结构定义，用于确定列的顺序和类型
     * @param record 要编码的记录，键为列名，值为对应数据
     * @return 编码后的二进制字节数组
     */
    static byte[] encode(TableSchema schema, Map<String,Object> record) {
        try {
            // 使用字节数组输出流作为缓冲区
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            // 使用数据输出流以便写入基本数据类型
            DataOutputStream dos = new DataOutputStream(bos);

            // 按照表结构中定义的列顺序进行编码
            List<ColumnDef> cols = schema.getColumns();
            for (ColumnDef cd : cols) {
                Object v = record.get(cd.getName());
                // 根据列的数据类型进行不同的编码处理
                if (cd.getType() == ColumnType.INT) {
                    // 整数类型：直接写入4字节int值
                    int iv = ((Number) v).intValue();
                    dos.writeInt(iv);
                } else if (cd.getType() == ColumnType.STRING) {
                    // 字符串类型：先写入长度，再写入UTF-8字节数据
                    byte[] bytes = ((String) v).getBytes(StandardCharsets.UTF_8);
                    dos.writeInt(bytes.length); // 写入字符串长度(4字节)
                    dos.write(bytes); // 写入字符串内容
                }
            }
            dos.flush();
            // 返回编码后的字节数组
            return bos.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException("Row encode failed", e);
        }
    }

    /**
     * 将二进制字节数组解码为一条记录
     * @param schema 表结构定义，用于确定列的顺序和类型
     * @param raw 要解码的二进制字节数组
     * @return 解码后的记录(Map<String, Object>)
     */
    static Map<String,Object> decode(TableSchema schema, byte[] raw) {
        try {
            // 使用LinkedHashMap保持列的顺序与表结构一致
            Map<String,Object> out = new LinkedHashMap<>();
            // 包装字节数组为输入流
            DataInputStream dis = new DataInputStream(new ByteArrayInputStream(raw));

            // 按照表结构中定义的列顺序进行解码
            for (ColumnDef cd : schema.getColumns()) {
                if (cd.getType() == ColumnType.INT) {
                    // 整数类型：读取4字节int值
                    int v = dis.readInt();
                    out.put(cd.getName(), v);
                } else if (cd.getType() == ColumnType.STRING) {
                    // 字符串类型：先读取长度，再读取对应长度的字节数据
                    int len = dis.readInt(); // 读取字符串长度
                    byte[] buf = new byte[len];
                    int r = dis.read(buf); // 读取字符串内容
                    if (r != len) throw new RuntimeException("Unexpected EOF decoding string");
                    out.put(cd.getName(), new String(buf, StandardCharsets.UTF_8));
                }
            }
            return out;
        } catch (Exception e) {
            throw new RuntimeException("Row decode failed", e);
        }
    }
} 